package org.study.dynamic.ds.strategy;

import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.study.dynamic.ds.constant.BusinessException;
import org.study.dynamic.ds.constant.ScreenModelEnum;
import org.study.dynamic.ds.dto.*;
import org.study.dynamic.ds.mapper.MysqlSqlParseMapper;
import org.study.dynamic.ds.mapper.SqlParseMapper;
import org.study.dynamic.ds.vo.HistogramTemplateVo;
import org.study.dynamic.ds.vo.ScreenModelVo;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 作用描述
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2023年06月09日
 */
@Slf4j
@RequiredArgsConstructor
@Component("mysql")
public class SqlParseStrategy4Mysql extends SqlParseStrategy {
    private final SqlParseMapper sqlParseMapper;
    private final MysqlSqlParseMapper mysqlSqlParseMapper;

    @Override
    public ScreenModelVo parseSql(ParseDto parseDto) {
        // 校验入参
        this.checkParam(parseDto);
        if (StringUtils.isBlank(parseDto.getSql())) {
            // 重写入参
            Optional<ParseColumnDto> rewriteOptional = parseDto.getDimension().stream()
                    .filter(e -> "year".equalsIgnoreCase(e.getSummaryMethod()) || "month".equalsIgnoreCase(e.getSummaryMethod()) || "day".equalsIgnoreCase(e.getSummaryMethod()) || "hour".equalsIgnoreCase(e.getSummaryMethod()))
                    .findAny();
            if (rewriteOptional.isPresent()) {
                doReWrite(parseDto);
            }

            // 重写指标排序
            if (CollectionUtils.isNotEmpty(parseDto.getDimension())) {
                List<ParseColumnDto> newIndexList = Lists.newArrayList();
                List<String> dimensionNameList = parseDto.getDimension().stream()
                        .map(ParseColumnDto::getColumnName)
                        .collect(Collectors.toList());
                newIndexList.addAll(parseDto.getDimension());
                newIndexList.addAll(parseDto.getIndex().stream()
                        .filter(e -> !dimensionNameList.contains(e.getColumnName()))
                        .collect(Collectors.toList()));
                parseDto.setIndex(newIndexList);
            }
        }

        // 手动设置目标数据源
        DynamicDataSourceContextHolder.push("test_mysql");
        // 查询结果
        //List<LinkedHashMap<String, Object>> data = sqlParseMapper.selectParseListFromMysql(parseDto);
        List<LinkedHashMap<String, Object>> data = mysqlSqlParseMapper.selectParseList(parseDto);
        // 清除手动设置的数据源
        DynamicDataSourceContextHolder.clear();

        // 转换结果
        Object result = new Object();
        if (StringUtils.isNotBlank(parseDto.getSql())) {
            result = convertSql(parseDto, data);
        } else {
            switch (parseDto.getTypeCode()) {
                case 1:
                    // 地图
                    result = convertMap(parseDto, data);
                    break;
                case 2:
                    // 柱状图
                    break;
                case 3:
                    // 折线图
                    break;
                case 4:
                    // 饼图
                    result = convertHistogram(parseDto, data);
                    break;
                case 5:
                    // 列表
                    result = convertTable(parseDto, data);
                    break;
                case 6:
                    // 散点图
                    result = convertScatter(parseDto, data);
                    break;
                default:
                    break;
            }
        }

        // 组装出参
        ScreenModelVo screenModelVo = new ScreenModelVo();
        screenModelVo.setTypeCode(parseDto.getTypeCode());
        screenModelVo.setTypeName(ScreenModelEnum.getValue(parseDto.getTypeCode()).getDesc());
        screenModelVo.setData(result);
        return screenModelVo;
    }

    private static void doReWrite(ParseDto parseDto) {
        // 必须是时间类型的字段
        List<ParseColumnDto> rewriteDimensionList = Lists.newArrayList();
        // 重写维度
        parseDto.getDimension().forEach(parseColumnDto -> {
            switch (parseColumnDto.getSummaryMethod()) {
                case "year":
                    String rewriteYear = "date_format(" + parseColumnDto.getColumnName() + ", '%Y')";
                    parseColumnDto.setColumnName(rewriteYear);
                    parseColumnDto.setSummaryMethod("");
                    rewriteDimensionList.add(
                            ParseColumnDto.builder()
                                    .columnName(rewriteYear)
                                    .columnComment(parseColumnDto.getColumnComment())
                                    .summaryMethod("")
                                    .build()
                    );
                    break;
                case "month":
                    String rewriteMonth = "date_format(" + parseColumnDto.getColumnName() + ", '%Y-%m')";
                    parseColumnDto.setColumnName(rewriteMonth);
                    parseColumnDto.setSummaryMethod("");
                    rewriteDimensionList.add(
                            ParseColumnDto.builder()
                                    .columnName(rewriteMonth)
                                    .columnComment(parseColumnDto.getColumnComment())
                                    .summaryMethod("")
                                    .build()
                    );
                    break;
                case "day":
                    String rewriteDay = "date_format(" + parseColumnDto.getColumnName() + ", '%Y-%m-%d')";
                    parseColumnDto.setColumnName(rewriteDay);
                    parseColumnDto.setSummaryMethod("");
                    rewriteDimensionList.add(
                            ParseColumnDto.builder()
                                    .columnName(rewriteDay)
                                    .columnComment(parseColumnDto.getColumnComment())
                                    .summaryMethod("")
                                    .build()
                    );
                    break;
                case "hour":
                    String rewriteHour = "date_format(" + parseColumnDto.getColumnName() + ", '%Y-%m-%d %H')";
                    parseColumnDto.setColumnName(rewriteHour);
                    parseColumnDto.setSummaryMethod("");
                    rewriteDimensionList.add(
                            ParseColumnDto.builder()
                                    .columnName(rewriteHour)
                                    .columnComment(parseColumnDto.getColumnComment())
                                    .summaryMethod("")
                                    .build()
                    );
                    break;
                default:
            }
        });
        if (CollectionUtils.isNotEmpty(rewriteDimensionList)) {
            // 重写指标
            List<ParseColumnDto> filterDimensionList = Lists.newArrayList();
            filterDimensionList.addAll(rewriteDimensionList);
            filterDimensionList.addAll(parseDto.getIndex().stream()
                    .filter(e -> !rewriteDimensionList.stream()
                            .map(ParseColumnDto::getColumnComment)
                            .collect(Collectors.toList())
                            .contains(e.getColumnComment()))
                    .collect(Collectors.toList()));
            parseDto.setIndex(filterDimensionList);
        }
    }

    @Override
    protected void checkParam(ParseDto parseDto) {
        // 模板类型编码必传
        if (null == parseDto.getTypeCode()) {
            throw new BusinessException("C1001", "参数错误：模板类型不能为空");
        }
        // 纯SQL校验
        if (StringUtils.isNotBlank(parseDto.getSql())) {
            // SQL安全性校验
            if (!parseDto.getSql().trim().matches("^\\s*(?i)SELECT\\b.*")) {
                throw new BusinessException("C1002", "SQL语句不合法，请检查SQL语句");
            }
        } else {
            // 表名
            if (StringUtils.isBlank(parseDto.getTableName())) {
                throw new BusinessException("C1003", "请选择一个数据源");
            }
            // 指标
            if (null == parseDto.getIndex() || CollectionUtils.isEmpty(parseDto.getIndex())) {
                throw new BusinessException("C1004", "请至少选择一个指标");
            } else {
                Optional<ParseColumnDto> emptyDimensionOptional = parseDto.getIndex().stream()
                        .filter(e -> StringUtils.isBlank(e.getColumnName()) || StringUtils.isBlank(e.getColumnComment()))
                        .findAny();
                if (emptyDimensionOptional.isPresent()) {
                    throw new BusinessException("C1005", "指标字段配置不完整，请检查");
                }
            }
            // 过滤
            if (null != parseDto.getFilter() && CollectionUtils.isNotEmpty(parseDto.getFilter())) {
                List<ParseFilterItemDto> filterItemList = Lists.newArrayList();
                parseDto.getFilter().forEach(e -> filterItemList.addAll(e.getFilters()));
                Optional<ParseFilterItemDto> errorParamOptional = filterItemList.stream()
                        .filter(e -> StringUtils.isBlank(e.getColumnName()) || StringUtils.isBlank(e.getFilterSymbol()) || StringUtils.isBlank(e.getFilterValue()))
                        .findAny();
                if (errorParamOptional.isPresent()) {
                    throw new BusinessException("C1006", "过滤字段配置不完整，请检查");
                }
            }
            // 排序规则
            if (null != parseDto.getOrderBy() && CollectionUtils.isNotEmpty(parseDto.getOrderBy())) {
                Optional<ParseOrderDto> errorParamOptional = parseDto.getOrderBy().stream()
                        .filter(e -> StringUtils.isBlank(e.getColumnName()) || StringUtils.isBlank(e.getOrderRule()))
                        .findAny();
                if (errorParamOptional.isPresent()) {
                    throw new BusinessException("C1006", "排序字段配置不完整，请检查");
                }
            }
        }
    }

    private Object convertSql(ParseDto parseDto, List<LinkedHashMap<String, Object>> data) {
        HistogramTemplateVo histogramTemplateVo = new HistogramTemplateVo();
        histogramTemplateVo.setDimensions(new ArrayList<>(data.get(0).keySet()));
        histogramTemplateVo.setSource(data);
        return histogramTemplateVo;
    }

    private Object convertMap(ParseDto parseDto, List<LinkedHashMap<String, Object>> data) {
        long count = parseDto.getIndex().stream()
                .filter(e -> "xcor".equalsIgnoreCase(e.getColumnName()) || "ycol".equalsIgnoreCase(e.getColumnName()))
                .count();
        if (2 == count) {
            List<LinkedHashMap<String, Object>> result = Lists.newArrayList();
            for (LinkedHashMap<String, Object> e : data) {
                e.put("position", Lists.newArrayList(e.get("经度"), e.get("纬度")));
                result.add(e);
            }
            return result;
        } else {
            return data;
        }
    }

    private Object convertScatter(ParseDto parseDto, List<LinkedHashMap<String, Object>> data) {
        return data;
    }

    private Object convertTable(ParseDto parseDto, List<LinkedHashMap<String, Object>> data) {
        return data;
    }

    private Object convertHistogram(ParseDto parseDto, List<LinkedHashMap<String, Object>> data) {
        List<String> dimensionConvertList = parseDto.getIndex().stream()
                .map(ParseColumnDto::getColumnComment)
                .collect(Collectors.toList());
        HistogramTemplateVo histogramTemplateVo = new HistogramTemplateVo();
        histogramTemplateVo.setDimensions(dimensionConvertList);
        histogramTemplateVo.setSource(data);
        return histogramTemplateVo;
    }
}
